"""
ComfyUI-xhh Custom Nodes
========================

自定义节点集合，包含各种实用工具节点

包含节点:
- TextListReader: 文本列表读取器
- LoraListSelector: LoRA列表选择器
- LoraSeletorDisplay: LoRA选择器显示 (交互式LoRA选择界面)
- OutpaintPanelV2: 外补面板V2 (可视化图片扩展工具)
- SimpleTranslatorNode: 简易翻译节点 (中英文智能翻译)
- ImageToPoseNode: 图片转骨架节点 (AI姿态检测)

Author: xhh
Version: 1.4.0
"""

import json
import os
import pymysql

from .nodes import TextListReader, LoraListSelector, OutpaintPanelV2, SimpleTranslatorNode, LoraListSelectorModelOnly, LoraSeletorDisplay, LoraSeletorDisplayV2, GetFileName

# 全局PromptManager实例，避免重复初始化
global_prompt_manager = None
mysql_connection_pool = None

def get_mysql_config():
    """获取MySQL配置"""
    return {
        'host': 'localhost',
        'port': 3306,
        'user': 'root',
        'password': 'root',
        'database': 'comfyui-xhh',
        'charset': 'utf8mb4'
    }

def check_image_file_exists(file_path):
    """检查图片文件是否存在"""
    if not file_path:
        print(f"[API] 文件路径为空")
        return False
    
    print(f"[API] 检查文件路径: {file_path}")
    
    try:
        # 处理不同的路径格式
        if file_path.startswith('http'):
            # 如果是HTTP URL，提取文件路径
            if '/api/xhh/files/' in file_path:
                file_path = file_path.split('/api/xhh/files/')[-1]
                print(f"[API] 从HTTP URL提取路径: {file_path}")
            else:
                print(f"[API] HTTP URL不包含预期路径")
                return False
        
        # 移除API路径前缀
        if file_path.startswith('/api/xhh/files/'):
            file_path = file_path.replace('/api/xhh/files/', '')
            print(f"[API] 移除API前缀后: {file_path}")
        
        # 标准化路径分隔符
        file_path = file_path.replace('\\', '/')
        
        # 构建基础文件目录
        current_dir = os.path.dirname(os.path.abspath(__file__))
        files_dir = os.path.join(current_dir, "..", "flux_prompt_helper", "promptManager-server", "files")
        print(f"[API] 基础文件目录: {files_dir}")
        
        # 构建可能的文件路径
        possible_paths = [
            os.path.join(files_dir, file_path),
            os.path.join(files_dir, "original_images", file_path),
        ]
        
        # 如果路径以 files/ 开头，尝试移除这个前缀
        if file_path.startswith('files/'):
            relative_path = file_path[6:]  # 移除 'files/' 前缀
            possible_paths.extend([
                os.path.join(files_dir, relative_path),
                os.path.join(files_dir, "original_images", relative_path),
            ])
        
        # 尝试模糊匹配文件名（处理时间戳差异）
        if '_original.' in file_path:
            # 提取文件名部分，忽略时间戳
            import re
            pattern = r'(\d+)_original\.(\w+)'
            match = re.search(pattern, file_path)
            if match:
                extension = match.group(2)
                # 在 original_images 目录中查找所有匹配的文件
                original_images_dir = os.path.join(files_dir, "original_images")
                if os.path.exists(original_images_dir):
                    for filename in os.listdir(original_images_dir):
                        if filename.endswith(f'_original.{extension}'):
                            possible_paths.append(os.path.join(original_images_dir, filename))
        
        print(f"[API] 可能的路径:")
        for i, path in enumerate(possible_paths):
            exists = os.path.exists(path)
            print(f"[API]   {i+1}. {path} - 存在: {exists}")
            if exists:
                print(f"[API] 找到匹配的文件: {path}")
                return True
        
        print(f"[API] 所有路径都不存在")
        return False
    except Exception as e:
        print(f"[API] 检查图片文件存在性失败: {e}")
        return False 

def get_mysql_connection():
    """获取MySQL数据库连接（使用连接池）"""
    global mysql_connection_pool
    
    try:
        if mysql_connection_pool is None:
            mysql_config = get_mysql_config()
            # 创建连接池
            try:
                mysql_connection_pool = pymysql.connect(
                    **mysql_config,
                    autocommit=True,
                    cursorclass=pymysql.cursors.DictCursor
                )
                print(f"[API] 创建MySQL连接池成功: {mysql_config['database']}")
            except pymysql.Error as e:
                print(f"[API] MySQL连接失败: {type(e).__name__}: {e}")
                import traceback
                traceback.print_exc()
                return None
        else:
            # 确保连接可用，必要时自动重连
            try:
                mysql_connection_pool.ping(reconnect=True)
            except Exception as e:
                print(f"[API] MySQL连接失效，尝试重建: {type(e).__name__}: {e}")
                mysql_config = get_mysql_config()
                try:
                    mysql_connection_pool = pymysql.connect(
                        **mysql_config,
                        autocommit=True,
                        cursorclass=pymysql.cursors.DictCursor
                    )
                    print("[API] MySQL连接已重建")
                except pymysql.Error as e:
                    print(f"[API] MySQL重连失败: {type(e).__name__}: {e}")
                    mysql_connection_pool = None
                    return None
        
        return mysql_connection_pool
    except Exception as e:
        print(f"[API] MySQL连接失败: {type(e).__name__}: {e}")
        import traceback
        traceback.print_exc()
        return None

def get_prompt_manager():
    global global_prompt_manager
    if global_prompt_manager is None:
        mysql_config = get_mysql_config()
        print(f"[API] 初始化PromptManager，使用MySQL数据库: {mysql_config['database']}")
        from .prompt_manager import PromptManager
        global_prompt_manager = PromptManager(mysql_config=mysql_config)
    return global_prompt_manager

# 节点映射
NODE_CLASS_MAPPINGS = {
    "XHH_TextListReader": TextListReader,
    "XHH_LoraListSelector": LoraListSelector,
    "XHH_LoraSeletorDisplayV2": LoraSeletorDisplayV2,
    "XHH_OutpaintPanelV2": OutpaintPanelV2,
    "XHH_LoraListSelectorModelOnly": LoraListSelectorModelOnly,
    "XHH_SimpleTranslator": SimpleTranslatorNode,
    "XHH_LoraSeletorDisplay": LoraSeletorDisplay,
    "XHH_GetFileName": GetFileName,
}

# 条件导入SaveImageExtendedXHH
try:
    from .save_image_extended_xhh import SaveImageExtendedXHH
    NODE_CLASS_MAPPINGS["XHH_SaveImageExtended"] = SaveImageExtendedXHH
    print("[ComfyUI-xhh] SaveImageExtendedXHH 可用")
except ImportError as e:
    print(f"[ComfyUI-xhh] SaveImageExtendedXHH 不可用: {e}")

# 显示名称映射
NODE_DISPLAY_NAME_MAPPINGS = {
    "XHH_TextListReader": "Text List Reader (XHH)",
    "XHH_LoraListSelector": "LoRA List Selector (XHH)",
    "XHH_OutpaintPanelV2": "Outpaint Panel V2 (XHH)",
    "XHH_LoraListSelectorModelOnly": "LoRA List Selector Model Only (XHH)",
    "XHH_SimpleTranslator": "Simple Translator (XHH)",
    "XHH_LoraSeletorDisplayV2": "LoRA Selector Display V    2 (XHH)",
    "XHH_LoraSeletorDisplay": "LoRA Selector Display (XHH)",
    "XHH_GetFileName": "Get File Name (XHH)",
}

# 条件添加SaveImageExtendedXHH的显示名称
try:
    from .save_image_extended_xhh import SaveImageExtendedXHH
    NODE_DISPLAY_NAME_MAPPINGS["XHH_SaveImageExtended"] = "💾 Save Image Extended XHH"
except ImportError:
    pass 

# 导出网页资源
WEB_DIRECTORY = "js"

# 注册API路由
def register_api_routes():
    """注册API路由"""
    print("[API] 开始注册API路由")
    try:
        from server import PromptServer
        from aiohttp import web
        import json
        import os
        import re
        import folder_paths
        
        print("[API] 成功导入所需模块")
        
        def convert_to_http_url(file_path, request):
            """将文件路径转换为完整的HTTP URL - 公共方法"""
            if not file_path:
                return None
            
            # 获取请求的主机和端口
            host = request.headers.get('Host', '127.0.0.1:8199')
            base_url = f"http://{host}"
            
            # 处理文件路径
            if file_path.startswith('http://') or file_path.startswith('https://'):
                # 已经是完整URL，直接返回
                return file_path
            elif file_path.startswith('/api/xhh/files/'):
                # 已经是API路径，直接拼接
                return f"{base_url}{file_path}"
            else:
                # 从绝对路径中提取文件名
                filename = os.path.basename(file_path)
                return f"{base_url}/api/xhh/files/{filename}"
        
        def convert_to_thumbnail_url(file_path, request):
            """将文件路径转换为缩略图HTTP URL - 公共方法"""
            if not file_path:
                return None
            
            # 获取请求的主机和端口
            host = request.headers.get('Host', '127.0.0.1:8199')
            base_url = f"http://{host}"
            
            # 从绝对路径中提取文件名
            filename = os.path.basename(file_path)
            return f"{base_url}/api/xhh/thum_files/{filename}"
        
        def process_image_data(image_data, request):
            """处理图片数据，转换所有路径为HTTP URL"""
            if not image_data:
                return image_data
            
            if isinstance(image_data, dict):
                # 处理图片对象
                processed = {}
                for key, value in image_data.items():
                    if key == 'file_path' and value:
                        processed[key] = convert_to_http_url(value, request)
                    elif key == 'thumbnail_path' and value:
                        processed[key] = convert_to_thumbnail_url(value, request)
                    else:
                        processed[key] = value
                return processed
            elif isinstance(image_data, list):
                # 处理图片列表
                return [process_image_data(item, request) for item in image_data]
            elif isinstance(image_data, str):
                # 处理字符串路径
                return convert_to_http_url(image_data, request)
            else:
                return image_data
        
        
        # 图片上传API
        @PromptServer.instance.routes.post("/api/xhh/upload-image")
        async def upload_image(request):
            """上传图片到提示词管理系统"""
            try:
                import os
                import uuid
                from datetime import datetime
                
                # 获取上传的文件
                data = await request.post()
                image_file = data.get('image')
                
                if not image_file:
                    return web.json_response({
                        "success": False,
                        "message": "没有上传文件"
                    })
                
                # 创建文件保存目录
                current_dir = os.path.dirname(os.path.abspath(__file__))
                files_dir = os.path.join(current_dir, "..", "flux_prompt_helper", "promptManager-server", "files")
                os.makedirs(files_dir, exist_ok=True)
                
                # 生成唯一文件名
                timestamp = int(datetime.now().timestamp() * 1000)
                file_id = str(uuid.uuid4())[:8]
                file_extension = os.path.splitext(image_file.filename)[1] if image_file.filename else '.jpg'
                filename = f"{timestamp}_{file_id}{file_extension}"
                
                # 保存文件
                file_path = os.path.join(files_dir, filename)
                with open(file_path, 'wb') as f:
                    f.write(image_file.file.read())
                
                # 生成缩略图
                try:
                    from PIL import Image
                    thumb_filename = f"{timestamp}_{file_id}_thumb.jpg"
                    thumb_path = os.path.join(files_dir, thumb_filename)
                    
                    with Image.open(file_path) as img:
                        # 创建缩略图
                        img.thumbnail((300, 300), Image.Resampling.LANCZOS)
                        img.save(thumb_path, 'JPEG', quality=85)
                    
                    print(f"[API] 图片上传成功: {filename}, 缩略图: {thumb_filename}")
                    
                    return web.json_response({
                        "success": True,
                        "image_url": f"/api/xhh/files/{filename}",
                        "thumb_url": f"/api/xhh/files/{thumb_filename}",
                        "filename": filename
                    })
                    
                except Exception as e:
                    print(f"[API] 缩略图生成失败: {e}")
                    return web.json_response({
                        "success": True,
                        "image_url": f"/api/xhh/files/{filename}",
                        "filename": filename
                    })
                
            except Exception as e:
                print(f"[API] 图片上传错误: {e}")
                return web.json_response({
                    "success": False,
                    "message": str(e)
                })
        
        print("[API] API路由注册成功: /api/xhh/upload-image")
        
        # 注册文件服务路由
        @PromptServer.instance.routes.get("/api/xhh/files/{file_path:.*}")
        async def serve_file(request):
            """提供文件服务 - 根据路径从数据库获取file_path"""
            try:
                file_path = request.match_info['file_path']
                print(f"[API] 请求文件: {file_path}")
                
                # 从数据库获取文件的实际路径
                conn = get_mysql_connection()
                if not conn:
                    return web.Response(status=500, text="Database connection failed")
                
                # 确保连接有效
                try:
                    conn.ping(reconnect=True)
                except Exception as e:
                    print(f"[API] MySQL连接不可用，重连: {e}")
                    conn = get_mysql_connection()
                    if not conn:
                        return web.Response(status=500, text="Database reconnection failed")

                def query_and_respond():
                    # 首先尝试精确匹配filename
                    with conn.cursor() as cursor:
                        cursor.execute("SELECT file_path, mime_type FROM images WHERE filename = %s", (file_path,))
                        result = cursor.fetchone()
                    
                    if not result:
                        # 如果精确匹配失败，尝试模糊匹配（忽略扩展名差异）
                        import re
                        name_without_ext = re.sub(r'\.[^.]+$', '', file_path)
                        print(f"[API] 尝试模糊匹配文件名主体: {name_without_ext}")
                        with conn.cursor() as cursor:
                            cursor.execute(
                                """
                                SELECT filename, file_path, mime_type FROM images 
                                WHERE filename LIKE %s
                                """,
                                (f"{name_without_ext}%",),
                            )
                            result = cursor.fetchone()
                        
                        if result:
                            print(f"[API] 模糊匹配成功: {result['filename']}")
                    if not result:
                        print(f"[API] 数据库中未找到文件: {file_path}")
                        return web.Response(status=404, text="File not found in database")
                    
                    actual_file_path = result['file_path']
                    mime_type = result.get('mime_type') or 'image/jpeg'
                    if not mime_type or mime_type == 'None':
                        mime_type = 'image/jpeg'
                    
                    print(f"[API] 数据库中的文件路径: {actual_file_path}")
                    print(f"[API] MIME类型: {mime_type}")
                    
                    if not os.path.exists(actual_file_path):
                        print(f"[API] 文件不存在于磁盘: {actual_file_path}")
                        return web.Response(status=404, text="File not found on disk")
                    
                    with open(actual_file_path, 'rb') as f:
                        file_data = f.read()
                    
                    headers = {
                        'Content-Type': mime_type,
                        'Content-Length': str(len(file_data)),
                        'Cache-Control': 'public, max-age=31536000'
                    }
                    print(f"[API] 返回文件: {file_path}, 大小: {len(file_data)} bytes")
                    return web.Response(body=file_data, headers=headers)

                # 首次尝试
                try:
                    return query_and_respond()
                except pymysql.err.InterfaceError as e:
                    print(f"[API] 数据库接口错误，尝试重连后重试: {e}")
                    conn = get_mysql_connection()
                    if not conn:
                        return web.Response(status=500, text="Database reconnection failed")
                    # 再次尝试一次
                    return query_and_respond()
                    
            except Exception as e:
                print(f"[API] 文件服务错误: {e}")
                import traceback
                traceback.print_exc()
                return web.Response(status=500, text="Internal server error")
        
        print("[API] 文件服务路由注册成功: /api/xhh/files/*")
        
        # 注册PromptManager API路由
        @PromptServer.instance.routes.post("/api/xhh/sync-loras")
        async def sync_loras(request):
            """手动同步LoRA列表"""
            try:
                import hashlib
                import os
                
                data = await request.json()
                lora_list = data.get('loras', [])
                
                print(f"[API] 开始同步 {len(lora_list)} 个LoRA")
                
                conn = get_mysql_connection()
                if not conn:
                    return web.json_response({"success": False, "error": "Database connection failed"})
                
                synced_count = 0
                updated_count = 0
                
                with conn.cursor() as cursor:
                    for lora_info in lora_list:
                        lora_path = lora_info.get('path', '')
                        lora_name = lora_info.get('name', '')
                        
                        if not lora_path or not os.path.exists(lora_path):
                            print(f"[API] LoRA文件不存在，跳过: {lora_path}")
                            continue
                        
                        # 计算文件MD5
                        try:
                            with open(lora_path, 'rb') as f:
                                file_content = f.read()
                                file_md5 = hashlib.md5(file_content).hexdigest()
                        except Exception as e:
                            print(f"[API] 计算MD5失败: {e}")
                            continue
                        
                        # 检查数据库中是否存在相同MD5的LoRA
                        cursor.execute("SELECT id, name, path FROM loras WHERE md5 = %s", (file_md5,))
                        existing_lora = cursor.fetchone()
                        
                        if existing_lora:
                            # 如果MD5相同但名称不同，更新名称
                            if existing_lora['name'] != lora_name:
                                cursor.execute("""
                                    UPDATE loras 
                                    SET name = %s, path = %s, updated_at = NOW()
                                    WHERE md5 = %s
                                """, (lora_name, lora_path, file_md5))
                                updated_count += 1
                                print(f"[API] 更新LoRA名称: {existing_lora['name']} -> {lora_name}")
                        else:
                            # 检查是否按路径存在
                            cursor.execute("SELECT id FROM loras WHERE path = %s", (lora_path,))
                            path_exists = cursor.fetchone()
                            
                            if path_exists:
                                # 路径存在但MD5不同，更新MD5
                                cursor.execute("""
                                    UPDATE loras 
                                    SET md5 = %s, updated_at = NOW()
                                    WHERE path = %s
                                """, (file_md5, lora_path))
                                updated_count += 1
                                print(f"[API] 更新LoRA MD5: {lora_path}")
                            else:
                                # 新增LoRA
                                lora_id = os.path.basename(lora_path)
                                cursor.execute("""
                                    INSERT INTO loras (id, name, path, md5, size, created_at, updated_at)
                                    VALUES (%s, %s, %s, %s, %s, NOW(), NOW())
                                """, (lora_id, lora_name, lora_path, file_md5, os.path.getsize(lora_path)))
                                synced_count += 1
                                print(f"[API] 新增LoRA: {lora_name}")
                
                conn.commit()
                
                result = {
                    "success": True,
                    "synced_count": synced_count,
                    "updated_count": updated_count,
                    "total_processed": len(lora_list)
                }
                
                print(f"[API] LoRA同步完成: 新增 {synced_count} 个，更新 {updated_count} 个")
                return web.json_response(result)
                
            except Exception as e:
                print(f"[API] LoRA同步失败: {e}")
                import traceback
                traceback.print_exc()
                return web.json_response({"success": False, "error": str(e)})
            finally:
                if conn:
                    pass  # 使用持久连接，不需要关闭

        @PromptServer.instance.routes.get("/api/xhh/loras")
        async def get_loras(request):
            """获取LoRA列表"""
            try:
                conn = get_mysql_connection()
                if not conn:
                    return web.json_response({"success": False, "error": "Database connection failed"})
                
                with conn.cursor() as cursor:
                    cursor.execute("""
                        SELECT id, name, path, size, tags, description, `trigger`, md5, is_favorite, created_at, updated_at
                        FROM loras 
                        ORDER BY updated_at DESC
                    """)
                    loras = cursor.fetchall()
                
                result = {
                    "success": True,
                    "loras": loras
                }
                
                print(f"[API] 获取到 {len(loras)} 个LoRA")
                return web.json_response(result)
                
            except Exception as e:
                print(f"[API] 获取LoRA列表失败: {e}")
                return web.json_response({"success": False, "error": str(e)})
            finally:
                if conn:
                    pass  # 使用持久连接，不需要关闭

        @PromptServer.instance.routes.get("/api/xhh/prompts")
        async def get_prompts(request):
            """获取提示词列表"""
            try:
                from .prompt_manager import PromptManager
                import os
                
                manager = get_prompt_manager()
                
                page = int(request.query.get('page', 1))
                per_page = int(request.query.get('per_page', 16))
                search = request.query.get('search', '')
                favorite_only = request.query.get('favorite_only', 'false').lower() == 'true'
                
                print(f"[API] 获取提示词列表 - page: {page}, per_page: {per_page}, search: {search}, favorite_only: {favorite_only}")
                
                # 计算offset
                offset = (page - 1) * per_page
                prompts = manager.get_prompts(limit=per_page, offset=offset, search=search, favorite_only=favorite_only)
                
                print(f"[API] 找到 {len(prompts)} 个提示词")
                
                # 获取总数（需要单独查询）
                total_count = manager.get_prompts_count(search=search, favorite_only=favorite_only)
                total_pages = (total_count + per_page - 1) // per_page
                
                # 转换数据格式以匹配原始API结构
                formatted_result = {
                    "success": True,
                    "data": {
                        "prompts": [],
                        "pagination": {
                            "page": page,
                            "per_page": per_page,
                            "total": total_count,
                            "pages": total_pages,
                            "has_next": page < total_pages,
                            "has_prev": page > 1
                        }
                    }
                }
                
                # 处理每个提示词，通过关系表获取图片
                for prompt in prompts:
                    prompt_id = prompt['id']
                    
                    # 通过关系表获取提示词的图片
                    manager = get_prompt_manager()
                    images = manager.get_prompt_images(prompt_id)
                    
                    print(f"[API] 提示词 {prompt_id} 获取到 {len(images)} 张图片")
                    for i, image in enumerate(images):
                        print(f"[API] 图片 {i+1}: file_path='{image.get('file_path', '')}', filename='{image.get('filename', '')}'")
                    
                    # 检查图片文件是否存在
                    valid_images = []
                    for image in images:
                        file_path = image.get('file_path', '')
                        print(f"[API] 检查图片文件: {file_path}")
                        if check_image_file_exists(file_path):
                            valid_images.append(image)
                            print(f"[API] 图片文件存在: {file_path}")
                        else:
                            print(f"[API] 图片文件不存在，跳过: {file_path}")
                            # 不再删除数据库记录，只是跳过
                            pass
                    
                    # 如果没有有效图片，仍然包含这个提示词，但图片列表为空
                    if not valid_images:
                        print(f"[API] 提示词 {prompt_id} 没有有效图片，但仍会显示")
                    
                    # 使用公共方法处理图片数据
                    formatted_images = process_image_data(valid_images, request)
                    
                    formatted_prompt = {
                        "id": prompt_id,
                        "chrome_id": prompt['chrome_id'],
                        "text": prompt['text'],
                        "source": prompt['source'],
                        "is_favorite": bool(prompt['is_favorite']),
                        "created_at": prompt['created_at'],
                        "updated_at": prompt['updated_at'],
                        "last_synced": prompt.get('last_synced', prompt['updated_at']),
                        "images": formatted_images,  # 转换后的图片列表
                        "image_count": len(formatted_images)
                    }
                    
                    # 为了向后兼容，设置第一张图片为image_data
                    if formatted_images:
                        formatted_prompt['image_data'] = formatted_images[0].get('file_path', '')
                        formatted_prompt['original_image'] = formatted_images[0].get('file_path', '')
                    else:
                        # 如果没有图片，设置空字符串
                        formatted_prompt['image_data'] = ''
                        formatted_prompt['original_image'] = ''
                    
                    formatted_result['data']['prompts'].append(formatted_prompt)
                
                print(f"[API] 格式化后的数据: {len(formatted_result['data']['prompts'])} 个提示词")
                return web.json_response(formatted_result)
                
            except Exception as e:
                print(f"[API] 获取提示词列表失败: {e}")
                import traceback
                traceback.print_exc()
                return web.json_response({"error": str(e), "prompts": []})
        
        @PromptServer.instance.routes.get("/api/xhh/files/{filename}")
        async def get_image_file(request):
            """根据filename获取图片文件"""
            try:
                filename = request.match_info.get('filename')
                if not filename:
                    return web.Response(status=400, text="Missing filename")
                
                print(f"[API] 请求图片文件: {filename}")
                
                # 从数据库获取图片信息
                conn = get_mysql_connection()
                if not conn:
                    return web.Response(status=500, text="Database connection failed")
                
                with conn.cursor() as cursor:
                    cursor.execute("SELECT file_path, mime_type FROM images WHERE filename = %s", (filename,))
                    result = cursor.fetchone()
                    
                    if not result:
                        print(f"[API] 图片文件不存在: {filename}")
                        return web.Response(status=404, text="Image not found")
                    
                    file_path, mime_type = result
                    print(f"[API] 找到图片文件: {file_path}")
                    
                    # 检查文件是否存在
                    if not os.path.exists(file_path):
                        print(f"[API] 图片文件不存在于磁盘: {file_path}")
                        return web.Response(status=404, text="Image file not found on disk")
                    
                    # 读取文件内容
                    with open(file_path, 'rb') as f:
                        image_data = f.read()
                    
                    # 设置响应头
                    headers = {
                        'Content-Type': mime_type or 'image/jpeg',
                        'Content-Length': str(len(image_data)),
                        'Cache-Control': 'public, max-age=31536000'  # 缓存1年
                    }
                    
                    print(f"[API] 返回图片文件: {filename}, 大小: {len(image_data)} bytes")
                    return web.Response(body=image_data, headers=headers)
                    
            except Exception as e:
                print(f"[API] 获取图片文件失败: {e}")
                import traceback
                traceback.print_exc()
                return web.Response(status=500, text="Internal server error")
            finally:
                pass  # 使用持久连接，不需要关闭
        
        @PromptServer.instance.routes.get("/api/xhh/thum_files/{filename}")
        async def get_thumbnail_file(request):
            """根据filename获取缩略图文件"""
            try:
                filename = request.match_info.get('filename')
                if not filename:
                    return web.Response(status=400, text="Missing filename")
                
                print(f"[API] 请求缩略图文件: {filename}")
                
                # 从数据库获取缩略图信息
                conn = get_mysql_connection()
                if not conn:
                    return web.Response(status=500, text="Database connection failed")
                
                with conn.cursor() as cursor:
                    cursor.execute("SELECT thumbnail_path FROM images WHERE filename = %s", (filename,))
                    result = cursor.fetchone()
                    
                    if not result:
                        print(f"[API] 缩略图文件不存在: {filename}")
                        return web.Response(status=404, text="Thumbnail not found")
                    
                    thumbnail_path = result[0]
                    print(f"[API] 找到缩略图文件: {thumbnail_path}")
                    
                    # 检查文件是否存在
                    if not thumbnail_path or not os.path.exists(thumbnail_path):
                        print(f"[API] 缩略图文件不存在于磁盘: {thumbnail_path}")
                        return web.Response(status=404, text="Thumbnail file not found on disk")
                    
                    # 读取文件内容
                    with open(thumbnail_path, 'rb') as f:
                        thumbnail_data = f.read()
                    
                    # 设置响应头
                    headers = {
                        'Content-Type': 'image/jpeg',
                        'Content-Length': str(len(thumbnail_data)),
                        'Cache-Control': 'public, max-age=31536000'  # 缓存1年
                    }
                    
                    print(f"[API] 返回缩略图文件: {filename}, 大小: {len(thumbnail_data)} bytes")
                    return web.Response(body=thumbnail_data, headers=headers)
                    
            except Exception as e:
                print(f"[API] 获取缩略图文件失败: {e}")
                import traceback
                traceback.print_exc()
                return web.Response(status=500, text="Internal server error")
            finally:
                pass  # 使用持久连接，不需要关闭

        @PromptServer.instance.routes.get("/api/xhh/prompts-v2")
        async def get_prompts_v2(request):
            """获取提示词列表（支持多对一关系）"""
            try:
                import pymysql
                import os
                
                mysql_config = get_mysql_config()
                
                page = int(request.query.get('page', 1))
                per_page = int(request.query.get('per_page', 16))
                search = request.query.get('search', '')
                favorite_only = request.query.get('favorite_only', 'false').lower() == 'true'
                
                try:
                    conn = pymysql.connect(**mysql_config)
                    cursor = conn.cursor()
                    
                    # 构建查询条件
                    where_conditions = []
                    params = []
                    
                    if search:
                        where_conditions.append("p.text LIKE %s")
                        params.append(f"%{search}%")
                    
                    if favorite_only:
                        where_conditions.append("p.is_favorite = 1")
                    
                    where_clause = "WHERE " + " AND ".join(where_conditions) if where_conditions else ""
                    
                    # 获取总数
                    count_query = f"""
                        SELECT COUNT(DISTINCT p.id) 
                        FROM prompts p 
                        {where_clause}
                    """
                    cursor.execute(count_query, params)
                    total = cursor.fetchone()[0]
                    
                    # 计算分页
                    offset = (page - 1) * per_page
                    
                    # 获取提示词列表，包含关联的图片
                    query = f"""
                        SELECT 
                            p.id,
                            p.chrome_id,
                            p.text,
                            p.source,
                            p.is_favorite,
                            p.created_at,
                            p.updated_at,
                            p.last_synced,
                            GROUP_CONCAT(i.file_path, '|') as image_paths
                        FROM prompts p
                        LEFT JOIN prompt_images pi ON p.id = pi.prompt_id
                        LEFT JOIN images i ON pi.image_id = i.id
                        {where_clause}
                        GROUP BY p.id
                        ORDER BY p.updated_at DESC
                        LIMIT %s OFFSET %s
                    """
                    
                    cursor.execute(query, params + [per_page, offset])
                    rows = cursor.fetchall()
                    
                    prompts = []
                    for row in rows:
                        prompt_id, chrome_id, text, source, is_favorite, created_at, updated_at, last_synced, image_paths = row
                        
                        # 使用公共方法处理图片路径
                        image_list = []
                        if image_paths:
                            for path in image_paths.split('|'):
                                if path:
                                    http_url = convert_to_http_url(path, request)
                                    if http_url:
                                        image_list.append(http_url)
                        
                        # 为了兼容性，保持原有的image_data字段（使用第一张图片）
                        image_data = image_list[0] if image_list else None
                        
                        prompt_data = {
                            "id": prompt_id,
                            "chrome_id": chrome_id,
                            "text": text,
                            "source": source,
                            "is_favorite": bool(is_favorite),
                            "created_at": created_at,
                            "updated_at": updated_at,
                            "last_synced": last_synced,
                            "image_data": image_data,
                            "image_list": image_list,
                            "image_count": len(image_list)
                        }
                        prompts.append(prompt_data)
                    
                    # 计算分页信息
                    total_pages = (total + per_page - 1) // per_page
                    has_next = page < total_pages
                    has_prev = page > 1
                    
                    result = {
                        "success": True,
                        "data": {
                            "prompts": prompts,
                            "pagination": {
                                "page": page,
                                "per_page": per_page,
                                "total": total,
                                "pages": total_pages,
                                "has_next": has_next,
                                "has_prev": has_prev
                            }
                        }
                    }
                    
                    print(f"[API] 返回 {len(prompts)} 条提示词，共 {total} 条")
                    return web.json_response(result)
                    
                except Exception as db_error:
                    print(f"[API] MySQL连接失败: {db_error}")
                    return web.json_response({"error": "数据库连接失败", "prompts": []})
                finally:
                    if 'conn' in locals():
                        conn.close()
                
            except Exception as e:
                print(f"[API] 获取提示词列表失败: {e}")
                import traceback
                traceback.print_exc()
                return web.json_response({"error": str(e), "prompts": []})
        
        @PromptServer.instance.routes.get("/api/xhh/prompts/{prompt_id}/images")
        async def get_prompt_images(request):
            """获取特定提示词的所有图片"""
            try:
                import pymysql
                
                prompt_id = int(request.match_info['prompt_id'])
                
                mysql_config = get_mysql_config()
                
                try:
                    conn = pymysql.connect(**mysql_config)
                    with conn.cursor() as cursor:
                        # 获取提示词信息
                        cursor.execute("SELECT id, text FROM prompts WHERE id = %s", (prompt_id,))
                        prompt = cursor.fetchone()
                        
                        if not prompt:
                            return web.json_response({"success": False, "error": "提示词不存在", "data": []})
                        
                        # 获取关联的图片
                        cursor.execute("""
                            SELECT i.id, i.filename, i.file_path, i.file_size, i.width, i.height, i.mime_type
                            FROM images i
                            JOIN prompt_images pi ON i.id = pi.image_id
                            WHERE pi.prompt_id = %s
                            ORDER BY pi.created_at DESC
                        """, (prompt_id,))
                        
                        images = []
                        for row in cursor.fetchall():
                            file_path = row[2].replace('\\', '/') if row[2] else None
                            filename = row[1]
                            
                            if file_path and filename:
                                # 使用公共方法转换路径
                                full_url = convert_to_http_url(file_path, request)
                                
                                # 生成缩略图和原图URL
                                if '_thumb' in filename:
                                    # 这是缩略图，生成原图URL
                                    original_path = file_path.replace('_thumb.jpg', '_original.png')
                                    original_url = convert_to_http_url(original_path, request)
                                    thumbnail_url = full_url
                                else:
                                    # 这是原图，直接使用原图图片
                                    thumbnail_url = full_url
                                    original_url = full_url
                                
                                images.append({
                                    "id": row[0],
                                    "filename": filename,
                                    "file_path": full_url,
                                    "thumbnail_url": thumbnail_url,
                                    "original_url": original_url,
                                    "file_size": row[3],
                                    "width": row[4],
                                    "height": row[5],
                                    "mime_type": row[6]
                                })
                            else:
                                # 如果路径或文件名为空，跳过
                                continue
                        
                        result = {
                            "success": True,
                            "data": images,
                            "images": images,  # 保持向后兼容
                            "prompt_id": prompt_id,
                            "prompt_text": prompt[1],
                            "image_count": len(images)
                        }
                        
                        return web.json_response(result)
                        
                except Exception as db_error:
                    print(f"[API] MySQL连接失败: {db_error}")
                    return web.json_response({"success": False, "error": "数据库连接失败", "data": []})
                finally:
                    if 'conn' in locals():
                        conn.close()
                
            except Exception as e:
                print(f"[API] 获取提示词图片失败: {e}")
                import traceback
                traceback.print_exc()
                return web.json_response({"success": False, "error": str(e), "data": []})
        
        @PromptServer.instance.routes.post("/api/xhh/prompts")
        async def add_prompt(request):
            """添加提示词"""
            try:
                data = await request.json()
                print(f"[API] 添加提示词: {data}")
                
                manager = get_prompt_manager()
                prompt_id = manager.add_prompt(
                    chrome_id=data.get('chrome_id', ''),
                    text=data.get('text', ''),
                    source=data.get('source', 'scrape'),
                    is_favorite=data.get('is_favorite', False)
                )
                
                if prompt_id:
                    return web.json_response({"success": True, "id": prompt_id})
                else:
                    return web.json_response({"success": False, "error": "添加失败"})
                    
            except Exception as e:
                print(f"[API] 添加提示词失败: {e}")
                return web.json_response({"success": False, "error": str(e)})
        
        @PromptServer.instance.routes.put("/api/xhh/prompts/{prompt_id}")
        async def update_prompt(request):
            """更新提示词"""
            try:
                prompt_id = int(request.match_info['prompt_id'])
                data = await request.json()
                print(f"[API] 更新提示词 {prompt_id}: {data}")
                
                manager = get_prompt_manager()
                success = manager.update_prompt(
                    prompt_id=prompt_id,
                    text=data.get('text'),
                    is_favorite=data.get('is_favorite')
                )
                
                return web.json_response({"success": success})
                
            except Exception as e:
                print(f"[API] 更新提示词失败: {e}")
                return web.json_response({"success": False, "error": str(e)})
        
        @PromptServer.instance.routes.delete("/api/xhh/prompts/{prompt_id}")
        async def delete_prompt(request):
            """删除提示词"""
            try:
                prompt_id = int(request.match_info['prompt_id'])
                print(f"[API] 删除提示词 {prompt_id}")
                
                manager = get_prompt_manager()
                success = manager.delete_prompt(prompt_id)
                
                return web.json_response({"success": success})
                
            except Exception as e:
                print(f"[API] 删除提示词失败: {e}")
                return web.json_response({"success": False, "error": str(e)})
        
        @PromptServer.instance.routes.post("/api/xhh/prompts/sync")
        async def sync_prompts(request):
            """同步Chrome插件数据"""
            try:
                data = await request.json()
                chrome_data = data.get('prompts', [])
                print(f"[API] 同步Chrome数据: {len(chrome_data)} 条记录")
                
                manager = get_prompt_manager()
                result = manager.sync_chrome_data(chrome_data)
                
                return web.json_response(result)
                
            except Exception as e:
                print(f"[API] 同步Chrome数据失败: {e}")
                return web.json_response({"success": False, "error": str(e)})
        
        @PromptServer.instance.routes.delete("/api/xhh/images/{image_id}")
        async def delete_image(request):
            """删除单个图片"""
            try:
                image_id = int(request.match_info['image_id'])
                print(f"[API] 删除图片 {image_id}")
                
                manager = get_prompt_manager()
                success = manager.delete_image_record(image_id)
                
                return web.json_response({"success": success})
                
            except Exception as e:
                print(f"[API] 删除图片失败: {e}")
                return web.json_response({"success": False, "error": str(e)})
        
        # 注册HTML页面路由
        @PromptServer.instance.routes.get("/xhh/prompt-manager")
        async def prompt_manager_page(request):
            """提示词管理器页面"""
            try:
                import os
                html_path = os.path.join(os.path.dirname(__file__), "web", "prompt_manager.html")
                if os.path.exists(html_path):
                    with open(html_path, 'r', encoding='utf-8') as f:
                        content = f.read()
                    return web.Response(text=content, content_type='text/html')
                else:
                    return web.Response(text="页面未找到", status=404)
            except Exception as e:
                print(f"[API] 加载提示词管理器页面失败: {e}")
                return web.Response(text="页面加载失败", status=500)
        
        # 管理界面路由
        @PromptServer.instance.routes.get("/xhh/manager/index.html")
        async def manager_index_page(request):
            """管理界面首页"""
            try:
                import os
                html_path = os.path.join(os.path.dirname(__file__), "web", "manager_index.html")
                if os.path.exists(html_path):
                    with open(html_path, 'r', encoding='utf-8') as f:
                        content = f.read()
                    return web.Response(text=content, content_type='text/html')
                else:
                    return web.Response(text="页面未找到", status=404)
            except Exception as e:
                print(f"[API] 加载管理界面首页失败: {e}")
                return web.Response(text="页面加载失败", status=500)
        
        @PromptServer.instance.routes.get("/xhh/manager/categories.html")
        async def categories_page(request):
            """基础分类页面"""
            try:
                import os
                html_path = os.path.join(os.path.dirname(__file__), "web", "categories.html")
                if os.path.exists(html_path):
                    with open(html_path, 'r', encoding='utf-8') as f:
                        content = f.read()
                    return web.Response(text=content, content_type='text/html')
                else:
                    return web.Response(text="页面未找到", status=404)
            except Exception as e:
                print(f"[API] 加载基础分类页面失败: {e}")
                return web.Response(text="页面加载失败", status=500)
        
        # LoRA管理相关API路由
        @PromptServer.instance.routes.get("/xhh/lora-manager")
        async def lora_manager_page(request):
            """LoRA管理器页面"""
            try:
                import os
                html_path = os.path.join(os.path.dirname(__file__), "web", "lora_manager.html")
                if os.path.exists(html_path):
                    with open(html_path, 'r', encoding='utf-8') as f:
                        content = f.read()
                    return web.Response(text=content, content_type='text/html')
                else:
                    return web.Response(text="页面未找到", status=404)
            except Exception as e:
                print(f"[API] 加载LoRA管理器页面失败: {e}")
                return web.Response(text="页面加载失败", status=500)
        
        @PromptServer.instance.routes.get("/api/xhh/lora-list")
        async def get_lora_list(request):
            """获取LoRA列表"""
            try:
                import pymysql
                
                mysql_config = get_mysql_config()
                
                try:
                    conn = get_mysql_connection()
                    if not conn:
                        print(f"[API] MySQL连接失败: 无法获取数据库连接")
                        return web.json_response({
                            "success": False,
                            "message": "数据库连接失败"
                        })
                    
                    with conn.cursor() as cursor:
                        # 获取LoRA列表（不包含images字段）
                        cursor.execute("""
                            SELECT id, name, path, size, tags, description, `trigger`, md5, is_favorite, created_at, updated_at
                            FROM loras
                            ORDER BY created_at DESC
                        """)
                        
                        loras = []
                        for row in cursor.fetchall():
                            lora_id = row['id']
                            
                            # 通过中间表获取LoRA的图片列表
                            cursor.execute("""
                                SELECT i.file_path
                                FROM images i
                                JOIN lora_images li ON i.id = li.image_id
                                WHERE li.lora_id = %s
                                ORDER BY li.created_at DESC
                            """, (lora_id,))
                            
                            image_paths = [img_row['file_path'] for img_row in cursor.fetchall()]
                            
                            # 使用公共方法转换图片路径
                            converted_images = [convert_to_http_url(path, request) for path in image_paths if path]
                            
                            # 安全解析tags字段
                            tags = []
                            if row.get('tags'):
                                try:
                                    tags = json.loads(row['tags'])
                                    if not isinstance(tags, list):
                                        tags = []
                                except (json.JSONDecodeError, TypeError):
                                    tags = []
                            
                            lora = {
                                "id": lora_id,
                                "name": row['name'],
                                "path": row['path'],
                                "size": row['size'],
                                "tags": tags,
                                "description": row.get('description', ''),
                                "images": converted_images,  # 转换后的图片列表
                                "trigger": row.get('trigger', ''),
                                "md5": row.get('md5', ''),
                                "is_favorite": bool(row.get('is_favorite', False)),
                                "created_at": row['created_at'].isoformat() if hasattr(row['created_at'], 'isoformat') else str(row['created_at']),
                                "updated_at": row['updated_at'].isoformat() if hasattr(row['updated_at'], 'isoformat') else str(row['updated_at'])
                            }
                            loras.append(lora)
                        
                        return web.json_response({
                            "success": True,
                            "data": loras,
                            "total": len(loras)
                        })
                        
                except Exception as db_error:
                    error_msg = str(db_error) if db_error else "未知错误"
                    error_type = type(db_error).__name__ if db_error else "UnknownError"
                    print(f"[API] MySQL连接失败: {error_type}: {error_msg}")
                    import traceback
                    traceback.print_exc()
                    return web.json_response({
                        "success": False,
                        "message": f"数据库连接失败: {error_msg}"
                    })
                
            except Exception as e:
                error_msg = str(e) if e else "未知错误"
                print(f"[API] 获取LoRA列表失败: {error_msg}")
                import traceback
                traceback.print_exc()
                return web.json_response({
                    "success": False,
                    "message": error_msg
                })
        
        @PromptServer.instance.routes.post("/api/xhh/lora-save")
        async def save_lora(request):
            """保存LoRA信息"""
            try:
                import pymysql
                import os
                import json
                
                # 获取数据（支持JSON和表单格式）
                data = {}
                content_type = request.headers.get('Content-Type', '')
                
                if 'application/json' in content_type:
                    # JSON格式
                    data = await request.json()
                elif 'multipart/form-data' in content_type:
                    # FormData格式
                    form_data = await request.post()
                    data = {}
                    for key, value in form_data.items():
                        if key == 'images' and isinstance(value, str):
                            # 处理images字段的JSON字符串
                            try:
                                data[key] = json.loads(value)
                            except:
                                data[key] = value
                        else:
                            data[key] = value
                else:
                    # 尝试JSON
                    try:
                        data = await request.json()
                    except:
                        # 尝试表单数据
                        form_data = await request.post()
                        data = dict(form_data)
                
                print(f"[API] 接收到的数据: {data}")
                
                mysql_config = get_mysql_config()
                
                try:
                    conn = pymysql.connect(**mysql_config)
                    with conn.cursor() as cursor:
                        # 解析标签和图片
                        tags = []
                        if 'tags' in data and data['tags']:
                            if isinstance(data['tags'], str):
                                tags = [tag.strip() for tag in data['tags'].split(',') if tag.strip()]
                            elif isinstance(data['tags'], list):
                                tags = data['tags']
                        
                        images = []
                        if 'images' in data:
                            if isinstance(data['images'], str):
                                try:
                                    images = json.loads(data['images'])
                                except:
                                    images = [data['images']] if data['images'] else []
                            elif isinstance(data['images'], list):
                                images = data['images']
                        
                        # 获取ID和路径，确保一致性
                        lora_id = data.get('id')
                        lora_path = data.get('path')
                        
                        # 如果只有id没有path，使用id作为path
                        if not lora_path and lora_id:
                            lora_path = lora_id
                        
                        # 如果只有path没有id，使用path作为id
                        if not lora_id and lora_path:
                            lora_id = lora_path
                        
                        # 确保路径是相对路径
                        if lora_path and lora_path.startswith('C:\\'):
                            # 转换为相对路径
                            lora_path = os.path.relpath(lora_path, os.path.join(os.getcwd(), 'models', 'loras'))
                            lora_path = lora_path.replace('\\', '/')
                            # 同时更新id为相对路径
                            lora_id = lora_path
                        
                        print(f"[API] 保存LoRA: ID={lora_id}, Path={lora_path}")
                        
                        # 更新或插入LoRA（不包含images字段）
                        cursor.execute("""
                            INSERT INTO loras 
                            (id, name, path, tags, description, `trigger`, prompt, updated_at)
                            VALUES (%s, %s, %s, %s, %s, %s, %s, NOW())
                            ON DUPLICATE KEY UPDATE
                            name = VALUES(name),
                            path = VALUES(path),
                            tags = VALUES(tags),
                            description = VALUES(description),
                            `trigger` = VALUES(`trigger`),
                            prompt = VALUES(prompt),
                            updated_at = NOW()
                        """, (
                            lora_id,
                            data.get('name'),
                            lora_path,
                            json.dumps(tags),
                            data.get('description', ''),
                            data.get('trigger', ''),
                            data.get('prompt', '')
                        ))
                        
                        # 处理图片关系（通过中间表）
                        if images:
                            # 先删除现有的图片关系
                            cursor.execute("DELETE FROM lora_images WHERE lora_id = %s", (lora_id,))
                            
                            # 为每个图片创建关系记录
                            for image_path in images:
                                # 查找或创建图片记录
                                cursor.execute("SELECT id FROM images WHERE file_path = %s", (image_path,))
                                image_record = cursor.fetchone()
                                
                                if image_record:
                                    image_id = image_record[0]
                                else:
                                    # 如果图片记录不存在，创建新的图片记录
                                    cursor.execute("""
                                        INSERT INTO images (file_path, filename, file_size, width, height, mime_type, created_at)
                                        VALUES (%s, %s, %s, %s, %s, %s, NOW())
                                    """, (
                                        image_path,
                                        os.path.basename(image_path),
                                        None,  # file_size
                                        None,  # width
                                        None,  # height
                                        None   # mime_type
                                    ))
                                    image_id = cursor.lastrowid
                                
                                # 创建LoRA和图片的关系
                                cursor.execute("""
                                    INSERT INTO lora_images (lora_id, image_id, created_at)
                                    VALUES (%s, %s, NOW())
                                    ON DUPLICATE KEY UPDATE created_at = NOW()
                                """, (lora_id, image_id))
                        
                        conn.commit()
                        
                        return web.json_response({
                            "success": True,
                            "message": "LoRA保存成功"
                        })
                        
                except Exception as db_error:
                    print(f"[API] MySQL连接失败: {db_error}")
                    return web.json_response({
                        "success": False,
                        "message": "数据库连接失败"
                    })
                finally:
                    if 'conn' in locals():
                        conn.close()
                
            except Exception as e:
                print(f"[API] 保存LoRA失败: {e}")
                return web.json_response({
                    "success": False,
                    "message": str(e)
                })

        @PromptServer.instance.routes.get("/api/xhh/lora-images")
        async def get_lora_images_by_name(request):
            """根据LoRA名称获取关联图片信息"""
            try:
                import pymysql
                import json
                from urllib.parse import unquote
                
                # 获取查询参数并解码URL编码
                lora_name = request.query.get('lora_name')
                if not lora_name:
                    return web.json_response({
                        "success": False,
                        "message": "缺少lora_name参数"
                    })
                
                # 解码URL编码的lora_name
                lora_name = unquote(lora_name)
                print(f"[API] 获取LoRA图片信息 - lora_name: {lora_name}")
                
                mysql_config = get_mysql_config()
                
                try:
                    conn = pymysql.connect(**mysql_config)
                    with conn.cursor() as cursor:
                        # 根据名称查找LoRA
                        cursor.execute("""
                            SELECT id, name, path, size, tags, description, `trigger`, md5, is_favorite, created_at, updated_at
                            FROM loras
                            WHERE name = %s
                        """, (lora_name,))
                        
                        lora_row = cursor.fetchone()
                        if not lora_row:
                            return web.json_response({
                                "success": True,
                                "data": {
                                    "lora_name": lora_name,
                                    "images": [],
                                    "lora_info": None
                                }
                            })
                        
                        lora_id = lora_row[0]
                        
                        # 通过关系表获取LoRA的图片列表
                        cursor.execute("""
                            SELECT i.file_path
                            FROM images i
                            JOIN lora_images li ON i.id = li.image_id
                            WHERE li.lora_id = %s
                            ORDER BY li.created_at DESC
                        """, (lora_id,))
                        
                        image_paths = [row[0] for row in cursor.fetchall()]
                        
                        # 使用公共方法转换图片路径
                        converted_images = [convert_to_http_url(path, request) for path in image_paths if path]
                        
                        # 安全解析tags字段
                        tags = []
                        if lora_row[4]:
                            try:
                                tags = json.loads(lora_row[4])
                                if not isinstance(tags, list):
                                    tags = []
                            except (json.JSONDecodeError, TypeError, ValueError):
                                tags = []
                        
                        # 解析LoRA信息
                        lora_info = {
                            "id": lora_row[0],
                            "name": lora_row[1],
                            "path": lora_row[2],
                            "size": lora_row[3],
                            "tags": tags,
                            "description": lora_row[5],
                            "trigger": lora_row[6],
                            "md5": lora_row[7],  # 修正：第7个字段是md5，不是prompt
                            "is_favorite": bool(lora_row[8]),
                            "created_at": lora_row[9].isoformat() if hasattr(lora_row[9], 'isoformat') else str(lora_row[9]),
                            "updated_at": lora_row[10].isoformat() if hasattr(lora_row[10], 'isoformat') else str(lora_row[10])
                        }
                        
                        return web.json_response({
                            "success": True,
                            "data": {
                                "lora_name": lora_name,
                                "images": converted_images,  # 转换后的图片列表
                                "lora_info": lora_info
                            }
                        })
                        
                except Exception as db_error:
                    error_msg = str(db_error) if db_error else "未知错误"
                    error_type = type(db_error).__name__ if db_error else "UnknownError"
                    print(f"[API] MySQL连接失败: {error_type}: {error_msg}")
                    import traceback
                    traceback.print_exc()
                    return web.json_response({
                        "success": False,
                        "message": f"数据库连接失败: {error_msg}"
                    })
                finally:
                    if 'conn' in locals():
                        conn.close()
                
            except Exception as e:
                error_msg = str(e) if e else "未知错误"
                print(f"[API] 获取LoRA图片信息失败: {error_msg}")
                import traceback
                traceback.print_exc()
                return web.json_response({
                    "success": False,
                    "message": error_msg
                })
        
        @PromptServer.instance.routes.get("/api/xhh/lora-files")
        async def get_lora_files(request):
            """获取本地LoRA文件列表"""
            try:
                # 获取LoRA目录路径
                lora_dir = os.path.join(os.getcwd(), 'models', 'loras')
                if not os.path.exists(lora_dir):
                    return web.json_response({
                        "success": False,
                        "error": "LoRA目录不存在"
                    })
                
                # 扫描LoRA文件
                lora_files = []
                for root, dirs, files in os.walk(lora_dir):
                    for file in files:
                        if file.lower().endswith(('.safetensors', '.pt')):
                            file_path = os.path.join(root, file)
                            file_name = os.path.splitext(file)[0]  # 去掉扩展名
                            
                            lora_files.append({
                                "path": file_path,
                                "name": file_name,
                                "size": os.path.getsize(file_path)
                            })
                
                print(f"[API] 找到 {len(lora_files)} 个本地LoRA文件")
                
                return web.json_response({
                    "success": True,
                    "files": lora_files,
                    "count": len(lora_files)
                })
                
            except Exception as e:
                print(f"[API] 获取LoRA文件列表失败: {e}")
                return web.json_response({
                    "success": False,
                    "error": str(e)
                })
        
        # 保存提示词API - 已合并到add_prompt函数中
        
        # 基础分类API
        @PromptServer.instance.routes.get("/api/xhh/categories")
        async def get_categories(request):
            """获取基础分类数据"""
            try:
                import pymysql
                import os
                
                mysql_config = get_mysql_config()
                
                try:
                    conn = pymysql.connect(**mysql_config)
                    with conn.cursor() as cursor:
                        # 获取分类数据
                        cursor.execute("SELECT category_key, category_name, description, data FROM categories")
                        categories = {}
                        
                        for row in cursor.fetchall():
                            category_key, category_name, description, data = row
                            categories[category_key] = {
                                "name": category_name,
                                "description": description,
                                **json.loads(data)
                            }
                        
                        return web.json_response({
                            "success": True,
                            "data": categories
                        })
                        
                except Exception as db_error:
                    print(f"[API] MySQL连接失败: {db_error}")
                    # 从JSON文件读取默认分类数据
                    try:
                        current_dir = os.path.dirname(os.path.abspath(__file__))
                        json_path = os.path.join(current_dir, "enhanced_categories.json")
                        if os.path.exists(json_path):
                            with open(json_path, 'r', encoding='utf-8') as f:
                                data = json.load(f)
                                default_categories = data.get('categories', {})
                        else:
                            default_categories = {}
                    except Exception as e:
                        print(f"[API] 读取分类JSON文件失败: {e}")
                        default_categories = {}
                    
                    return web.json_response({
                        "success": True,
                        "data": default_categories
                    })
                finally:
                    if 'conn' in locals():
                        conn.close()
                
            except Exception as e:
                print(f"[API] 获取分类数据失败: {e}")
                return web.json_response({
                    "success": False,
                    "message": str(e)
                })
        
        @PromptServer.instance.routes.post("/api/xhh/generate-image")
        async def generate_image(request):
            """生成测试图片"""
            try:
                data = await request.json()
                prompt = data.get('prompt', '')
                model = data.get('model', 'flux')
                items = data.get('items', [])
                
                print(f"[API] 生成图片请求 - 模型: {model}, 提示词: {prompt}")
                
                # 这里应该调用相应的模型生成图片
                # 由于这是演示，我们返回一个模拟的响应
                result = {
                    "success": True,
                    "data": {
                        "image_url": f"/api/xhh/placeholder/{model}.jpg",
                        "prompt": prompt,
                        "model": model,
                        "items": items
                    }
                }
                
                return web.json_response(result)
                
            except Exception as e:
                print(f"[API] 生成图片失败: {e}")
                return web.json_response({
                    "success": False,
                    "message": str(e)
                })
        
        @PromptServer.instance.routes.get("/api/xhh/image-prompt-info")
        async def get_image_prompt_info(request):
            """获取图片的提示词信息"""
            try:
                import pymysql
                from urllib.parse import unquote
                
                # 获取图片文件名参数
                image = request.query.get('image', '')
                if not image:
                    return web.json_response({
                        'success': False,
                        'message': '缺少图片参数'
                    })
                
                # 解码URL编码的文件名
                image = unquote(image)
                print(f"[API] 获取图片提示词信息: {image}")
                
                mysql_config = get_mysql_config()
                
                try:
                    conn = pymysql.connect(**mysql_config)
                    with conn.cursor() as cursor:
                        # 从URL中提取文件名
                        filename = image.split('/')[-1]
                                                
                        print(f"[API] 提取的文件名: {filename}")
                        
                        # 使用文件名进行左匹配查询
                        cursor.execute("SELECT id FROM images WHERE file_path LIKE %s", (f'%{filename}',))
                        image_record = cursor.fetchone()
                        
                        if not image_record:
                            print(f"[API] 未找到匹配的图片记录: {filename}")
                            return web.json_response({
                                'success': True,
                                'data': {
                                    'text': '暂无提示词信息',
                                    'prompt_id': None
                                }
                            })
                        
                        image_id = image_record[0]
                        print(f"[API] 找到匹配的图片记录: ID={image_id}")
                        
                        # 通过关系表查询提示词
                        cursor.execute("""
                            SELECT p.id, p.text, p.is_favorite, p.source, p.created_at
                            FROM prompts p
                            JOIN prompt_images pi ON p.id = pi.prompt_id
                            WHERE pi.image_id = %s
                            ORDER BY pi.created_at DESC
                            LIMIT 1
                        """, (image_id,))
                        
                        prompt = cursor.fetchone()
                        
                        if prompt:
                            # 处理datetime对象，转换为字符串
                            created_at = prompt[4]
                            if hasattr(created_at, 'isoformat'):
                                created_at = created_at.isoformat()
                            elif created_at is not None:
                                created_at = str(created_at)
                            
                            return web.json_response({
                                'success': True,
                                'data': {
                                    'text': prompt[1],
                                    'prompt_id': prompt[0],
                                    'is_favorite': bool(prompt[2]),
                                    'source': prompt[3],
                                    'created_at': created_at
                                }
                            })
                        else:
                            return web.json_response({
                                'success': True,
                                'data': {
                                    'text': '暂无提示词信息',
                                    'prompt_id': None
                                }
                            })
                            
                except Exception as db_error:
                    print(f"[API] MySQL连接失败: {db_error}")
                    return web.json_response({
                        'success': False,
                        'message': '数据库连接失败'
                    })
                finally:
                    if 'conn' in locals():
                        conn.close()
                        
            except Exception as e:
                print(f"[API] 获取图片提示词信息失败: {e}")
                import traceback
                traceback.print_exc()
                return web.json_response({
                    'success': False,
                    'message': f'获取图片提示词信息失败: {str(e)}'
                })

        @PromptServer.instance.routes.get("/api/xhh/images")
        async def get_images(request):
            """获取图片列表"""
            try:
                # 返回模拟数据
                return web.json_response({
                    "success": True,
                    "data": []
                })
            except Exception as e:
                print(f"[API] 获取图片列表失败: {e}")
                return web.json_response({
                    "success": False,
                    "message": str(e)
                })
        
        print("[API] PromptManager API路由注册成功")
        print("[API] LoRA管理API路由注册成功")
        print("[API] 基础分类API路由注册成功")
        
    except ImportError as e:
        print(f"[API] 无法导入PromptServer，API路由未注册: {e}")
    except Exception as e:
        print(f"[API] API路由注册失败: {e}")
        import traceback
        traceback.print_exc()

# 注册API路由
register_api_routes()

# 注册JavaScript扩展
def register_js_extensions():
    """注册JavaScript扩展"""
    try:
        # 使用ComfyUI的扩展注册方式
        from comfy_extras import app
        
        # 注册LoRA管理器按钮扩展
        app.registerExtension({
            "name": "ComfyUI-xhh.lora_manager_button",
            "src": "lora_manager_button.js"
        })
        
        # 注册LoRA管理器按钮扩展 V2版本
        app.registerExtension({
            "name": "ComfyUI-xhh.lora_manager_button_v2",
            "src": "lora_manager_button_v2.js"
        })
        
        print("✅ JavaScript扩展注册成功")
    except Exception as e:
        print(f"❌ JavaScript扩展注册失败: {e}")

# 注册JavaScript扩展
register_js_extensions()

# 注册新的API路由
try:
    from .api_routes import DatabaseManager, register_new_routes
    
    # 创建数据库管理器
    db_manager = DatabaseManager()
    
    # 注册新的API路由
    from server import PromptServer
    if hasattr(PromptServer, 'instance') and PromptServer.instance:
        register_new_routes(PromptServer.instance.app, db_manager)
        print("✅ 新API路由注册成功")
    else:
        print("⚠️ PromptServer实例不可用，跳过新API路由注册")
        
except Exception as e:
    print(f"❌ 新API路由注册失败: {e}")

__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']

print(f"🚀 ComfyUI-xhh v1.4.0 加载完成")
print(f"📦 包含节点: {len(NODE_CLASS_MAPPINGS)} 个")
for name, display_name in NODE_DISPLAY_NAME_MAPPINGS.items():
    print(f"  • {display_name}")
print(f"🌐 前端资源目录: {WEB_DIRECTORY}")
print(f"✨ 新增: 交互式LoRA选择器显示功能") 